package org.gtugs.omsk.funnycats.scenes;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.cocos2d.actions.UpdateCallback;
import org.cocos2d.layers.CCLayer;
import org.cocos2d.layers.CCScene;
import org.cocos2d.nodes.CCDirector;
import org.cocos2d.nodes.CCSprite;
import org.cocos2d.types.CGPoint;
import org.cocos2d.types.CGSize;
import org.gtugs.omsk.funnycats.application.FunnyCatsApplication;
import org.gtugs.omsk.funnycats.engine.GameEffect.EffectType;
import org.gtugs.omsk.funnycats.engine.GameObject;
import org.gtugs.omsk.funnycats.engine.GameWorld;
import org.gtugs.omsk.funnycats.engine.GameWorld.ContactEvent;
import org.gtugs.omsk.funnycats.engine.GameWorld.ContactListener;
import org.gtugs.omsk.funnycats.engine.GameWorld.DirectionChangedEvent;
import org.gtugs.omsk.funnycats.engine.GameWorld.OnXDirectionChangedListener;
import org.gtugs.omsk.funnycats.engine.beans.Position;
import org.gtugs.omsk.funnycats.engine.beans.Size;
import org.gtugs.omsk.funnycats.engine.beans.Vector;
import org.gtugs.omsk.funnycats.scenes.classes.FunnyConverter;
import org.gtugs.omsk.funnycats.scenes.classes.FunnyScore;
import org.gtugs.omsk.funnycats.scenes.classes.FunnyUserData;
import org.gtugs.omsk.funnycats.scenes.classes.FunnyUserData.ObjectType;

import android.view.MotionEvent;

public class GameScene extends CCLayer implements UpdateCallback{
	
	public static CCScene scene() {
		CCScene scene = CCScene.node();
		
		CCSprite bg = CCSprite.sprite("background.png");
		final float backgroundScale = (float) CCDirector.sharedDirector().winSize().getHeight() / bg.getContentSize().height;
		bg.setScale(backgroundScale);
		bg.setPosition(CCDirector.sharedDirector().winSize().width/2, CCDirector.sharedDirector().winSize().height/2);
		scene.addChild(bg);
		
		CCLayer layer = new GameScene();
		scene.addChild(layer);
		
		return scene;
	}

	public GameScene(){
		FunnyCatsApplication.Log.d(TAG, "game scene constructor");
		initScene();
		scheduleUpdate();
	}

	protected void initScene() {
		this.setIsTouchEnabled(true);
		initWorld();
		initInterface();		
	}
	
	protected void initInterface(){
		mInterface = new GameInterface();
		
		mInterface.setTime(mScore.getTimeToEnd());
		mInterface.setScore(mScore.getScore());
		
		addChild(mInterface);		
	}
	
	protected void initWorld(){
		mWorld = new GameWorld(Size.make(3, 2.25f));
		
		final CGSize cgDisplaySize = CCDirector.sharedDirector().winSize();
		final Size displaySize = Size.make(cgDisplaySize.width, cgDisplaySize.height);
		mConverter = FunnyConverter.scaleInCenter(mWorld.getSize(), displaySize);
		
		final FunnyUserData mouseUserData = new FunnyUserData(CCSprite.sprite("mouse_64.png"), FunnyUserData.ObjectType.MOUSE);
		final GameObject mouse = new GameObject(3, mouseUserData, Size.make(0.3f, 0.3f));
		mMouse = mouse;
		add(mouse,  Position.make(1.5f, 1.125f));
		
		final FunnyUserData redCatUserData = new FunnyUserData(CCSprite.sprite("cat_red_left.png"), FunnyUserData.ObjectType.CAT);
		redCatUserData.getSprite().setFlipX(true);
		final GameObject redCat = new GameObject(4, redCatUserData, Size.make(0.6f, 0.4f));
		add(redCat,  Position.make(0.5f, 0.5f), Vector.makeFromAngle(0.5f, (float) (Math.PI / 3.0f)));
		
		final FunnyUserData blackCatUserData = new FunnyUserData(CCSprite.sprite("cat_black_left.png"), FunnyUserData.ObjectType.CAT);
		final GameObject blackCat = new GameObject(4, blackCatUserData, Size.make(0.6f, 0.4f));
		add(blackCat, Position.make(0.4f, 1.9f), Vector.makeFromAngle(0.5f, (float) (-Math.PI / 8.0f)));

		mWorld.addContactListener(new ContactListener() {	
			@Override
			public void contant(ContactEvent contactEvent) {
				final GameObject firstObject = contactEvent.firstObject;
				final GameObject secondObject = contactEvent.secondObject;
				
				final FunnyUserData firstObjectUserData = (FunnyUserData)firstObject.getUserData();
				final FunnyUserData secondObjectUserData = (FunnyUserData)secondObject.getUserData();
				
				final GameObject mouse = firstObjectUserData.isMouse() ? firstObject : (secondObjectUserData.isMouse() ? secondObject : null);
				final GameObject cat = firstObjectUserData.isCat() ? firstObject : (secondObjectUserData.isCat() ? secondObject : null);
				final GameObject whiskas = firstObjectUserData.isWhiskas() ? firstObject : (secondObjectUserData.isWhiskas() ? secondObject : null);
				final GameObject cheese = firstObjectUserData.isCheese() ? firstObject : (secondObjectUserData.isCheese() ? secondObject : null);
				final GameObject clew = firstObjectUserData.isClew() ? firstObject : (secondObjectUserData.isClew() ? secondObject : null);
								
				if(mouse != null){
					if(cat != null){
						catWins();
						return;
					}
					
					if(whiskas != null){
						mScore.setScore(mScore.getScore() + 500);
						mScore.decLifeTime(5.0f);
						remove(whiskas);
						return;
					}
					
					if(cheese != null){
						mScore.setScore(mScore.getScore() + 1000);
						mScore.decLifeTime(20.0f);
						remove(cheese);
						return;
					}
					
					if(clew != null){
						mScore.setScore(mScore.getScore() + 250);
						remove(clew);
						return;
					}
				}
				
				if(cat != null){
					if(whiskas != null){
						mWorld.setSpeedForObject(cat, cat.getSpeed().length() + 0.1f);
						remove(whiskas);
						return;
					}
					
					if(cheese != null){
						remove(cheese);
						return;
					}
					
					if(clew != null){
						mWorld.setEffect(cat, EffectType.STOP);
						remove(clew);
						return;
					}
				}
			}
		});
		
		mWorld.addOnXDirectionChangedListener(new OnXDirectionChangedListener() {
			@Override
			public void onDirectionChanged(DirectionChangedEvent event) {
				final FunnyUserData userData = (FunnyUserData)event.directionChangedObject.getUserData();
				if (userData.isCat()){
					CCSprite catSprite = userData.getSprite();
					catSprite.setFlipX(!catSprite.getFlipX());
				}
			}
		});		
	}
	
	@Override
	public boolean ccTouchesBegan(MotionEvent event) {
		mIsPause = false;
		CGPoint p = CCDirector.sharedDirector().convertToGL(CGPoint.make(event.getX(), event.getY()));
		mWorld.setTargetForObject(mMouse, mConverter.positonDisplayToWorld(Position.make(p.x, p.y)));
		return true;
	}

	@Override
	public boolean ccTouchesMoved(MotionEvent event) {
		mIsPause = false;
		CGPoint p = CCDirector.sharedDirector().convertToGL(CGPoint.make(event.getX(), event.getY()));
		mWorld.setTargetForObject(mMouse, mConverter.positonDisplayToWorld(Position.make(p.x, p.y)));
		return true;
	}

	@Override
	public boolean ccTouchesEnded(MotionEvent event) {
		/*mIsPause = false;
		CGPoint p = CCDirector.sharedDirector().convertToGL(CGPoint.make(event.getX(), event.getY()));
		mWorld.setTargetForObject(mMouse, mConverter.positonDisplayToWorld(Position.make(p.x, p.y)));
		return true;*/
		return super.ccTouchesEnded(event);
	}
	
	@Override
	public void update(float dt) {
		List<GameObject> removeObjectsList = new ArrayList<GameObject>();
		
		if(mIsPause == true){
			return;
		}
		
		mWorld.step(dt);
		mScore.step(dt);
		
		if(mScore.getGameLifetime() >= timeOfNextWisckas){
			timeOfNextWisckas += randomTimeOfNextWhiskas();
			addExtraObject(ObjectType.WHISKAS);
		}
		
		if(mScore.getGameLifetime() >= timeOfNextCheese){
			timeOfNextCheese += randomTimeOfNextCheese();
			addExtraObject(ObjectType.CHEESE);
		}
		
		if(mScore.getGameLifetime() >= timeOfNextClew){
			timeOfNextClew += randomTimeOfNextClew();
			addExtraObject(ObjectType.CLEW);
		}
		
		mInterface.setScore(mScore.getScore());
		mInterface.setTime(mScore.getTimeToEnd());
		
		if(mScore.isGameOver()){
			mouseWin();
		}
		
		for(GameObject object : mWorld.getGameObjects()){
			if(object.isLifetimeExist() && (object.getLifetime() < 0.0f)){
				removeObjectsList.add(object);
				continue;
			}
			
			final FunnyUserData userData = (FunnyUserData)object.getUserData();
			setSpritePosition(userData.getSprite(), object.getCenterPosition());
		}
		
		for(GameObject object: removeObjectsList){
			remove(object);
		}
	}
	
	protected void catWins(){
		CCDirector.sharedDirector().pushScene(GameOverScene.scene("Cats win!"));
	}
	
	protected void mouseWin(){
		CCDirector.sharedDirector().pushScene(GameOverScene.scene("Your score:" + mScore.getScore()));
	}
	
	/*
	protected void scheduleAddWhiskas(){
		CCScheduler.sharedScheduler().unschedule("addWhiskas", this);
		int nextWhiskas = mRandom.nextInt(8) + 8;
		CCScheduler.sharedScheduler().schedule("addWhiskas", this, nextWhiskas, false);
	}
	*/
	
	public void addExtraObject(ObjectType type){
		final CCSprite sprite;
		final Size size;
		final float lifetime = 7.0f;
		
		switch(type){
		case CHEESE:
			sprite = CCSprite.sprite("cheese.png");
			size = Size.make(0.4f, 0.4f);
			break;
		case CLEW:
			sprite = CCSprite.sprite("clew.png");
			size = Size.make(0.4f, 0.4f);
			break;
		case WHISKAS:
			sprite = CCSprite.sprite("whiskas.png");
			size = Size.make(0.2f, 0.2f);
			break;
		default:
			sprite = null;
			size = null;
		}
		
		final FunnyUserData data = new FunnyUserData(sprite, type);
		final GameObject object = new GameObject(0, data, size);
		add(object, getRandomPointInWorld(), lifetime);
	}
	
	/*
	public void addWhiskas(float dt){
		Log.d(TAG, "addWhiskas " + dt);
		final FunnyUserData whiskasUserData = new FunnyUserData(CCSprite.sprite("whiskas.png"), ObjectType.WHISKAS);
		final GameObject whiskas = new GameObject(0, whiskasUserData, Size.make(0.15f, 0.3f));
		add(whiskas, getRandomPointInWorld());
		scheduleAddWhiskas();
	}
	*/
	
	protected void add(GameObject gameObject, Position position){
		final FunnyUserData userData = (FunnyUserData)gameObject.getUserData();
		final Size size = gameObject.getOccupiedArea().size;
		scaleSprite(userData.getSprite(), size);
		setSpritePosition(userData.getSprite(), position);
		addChild(userData.getSprite());
		mWorld.addGameObject(gameObject, position);
	}
	
	protected void add(GameObject gameObject, Position position, Vector speed){
		final FunnyUserData userData = (FunnyUserData)gameObject.getUserData();
		final Size size = gameObject.getOccupiedArea().size;
		scaleSprite(userData.getSprite(), size);
		setSpritePosition(userData.getSprite(), position);
		addChild(userData.getSprite());
		
		mWorld.setBasicSpeedForObject(gameObject, Vector.make(speed.x * 0.8f, speed.y * 0.8f));
		
		mWorld.addGameObject(gameObject, position, speed);
	}
	
	protected void add(GameObject gameObject, Position position, float lifetime){
		final FunnyUserData userData = (FunnyUserData)gameObject.getUserData();
		final Size size = gameObject.getOccupiedArea().size;
		scaleSprite(userData.getSprite(), size);
		setSpritePosition(userData.getSprite(), position);
		addChild(userData.getSprite());
		mWorld.addGameObject(gameObject, position, lifetime);
	}
	
	protected void remove(GameObject gameObject){
		final FunnyUserData userData = (FunnyUserData)gameObject.getUserData();
		mWorld.removeObject(gameObject);
		removeChild(userData.getSprite(), true);
	}
	
	private void setSpritePosition(CCSprite sprite, Position position){
		Position p = mConverter.positionWorldToDisplay(Position.make(position.x, position.y));
		sprite.setPosition(p.x, p.y);
	}
	
	protected void scaleSprite(CCSprite sprite, Size size){
		final Size originalTextureSize = Size.make(sprite.getTexture().getWidth(), sprite.getTexture().getHeight());
		final Size wantedTextureSize = mConverter.sizeWorldToDisplay(size);
		final float scale = wantedTextureSize.width / originalTextureSize.width;
		sprite.setScale(scale);
	}
	
	protected Position getRandomPointInWorld(){
		final Size size = mWorld.getSize();
		float x = mRandom.nextFloat() * size.width;
		float y = mRandom.nextFloat() * size.height;
		return Position.make(x, y);
	}
	
	protected float randomTimeOfNextWhiskas(){
		return 10.0f*((float)Math.random() + 0.5f);
	}
	
	protected float randomTimeOfNextCheese(){
		return 10.0f*((float)Math.random() + 0.5f);
	}
	
	protected float randomTimeOfNextClew(){
		return 20.0f*((float)Math.random() + 0.5f);
	}
	
/*	protected CGPoint getRandomPointInDisplay(){
		final CGSize size = CCDirector.sharedDirector().winSize();
		float x = mRandom.nextFloat() * size.width;
		float y = mRandom.nextFloat() * size.height;
		Log.d(TAG, x + ", " + y);
		return CGPoint.make(x, y);
	}
*/	
	private boolean mIsPause = true;	
	private FunnyScore mScore = new FunnyScore();
	
	private GameInterface mInterface;
	
	private float timeOfNextWisckas = randomTimeOfNextWhiskas();
	private float timeOfNextCheese = randomTimeOfNextCheese();
	private float timeOfNextClew = randomTimeOfNextClew();
	
	private static final String TAG = GameScene.class.getSimpleName();
	private GameObject mMouse;
	private GameWorld mWorld;
	private FunnyConverter mConverter;
	private Random mRandom = new Random(System.currentTimeMillis());
}
